<template>
  <div ref="waterfallWrapper" class="waterfall-list" :style="{ height: `${wrapperHeight}px` }">
    <div v-for="(item, index) in list" :key="getKey(item, index)" class="waterfall-item">
      <slot name="item" :item="item" :index="index" :url="getRenderURL(item)" />
    </div>
  </div>
</template>

<script>
import { ref, watch, onMounted, onUnmounted, provide, nextTick } from "vue";

export default {
  name: 'WaterfallList',
  props: {
    list: {
      type: Array,
      required: true,
      default: () => []
    },
    rowKey: {
      type: String,
      default: "id"
    },
    imgSelector: {
      type: String,
      default: "src"
    },
    width: {
      type: Number,
      default: 200
    },
    breakpoints: {
      type: Object,
      default: () => ({
        1200: { rowPerView: 4 },
        800: { rowPerView: 3 },
        500: { rowPerView: 2 }
      })
    },
    gutter: {
      type: Number,
      default: 10
    },
    hasAroundGutter: {
      type: Boolean,
      default: true
    },
    posDuration: {
      type: Number,
      default: 300
    },
    align: {
      type: String,
      default: "center",
      validator: (value) => ['left', 'center', 'right'].includes(value)
    },
    lazyLoad: {
      type: Boolean,
      default: true
    },
    crossOrigin: {
      type: Boolean,
      default: true
    },
    delay: {
      type: Number,
      default: 300
    },
    loadingImg: {
      type: String,
      default: ''
    },
    errorImg: {
      type: String,
      default: ''
    }
  },

  setup(props, { emit }) {
    const waterfallWrapper = ref(null);
    const wrapperWidth = ref(0);
    const colWidth = ref(0);
    const cols = ref(0);
    const offsetX = ref(0);
    const wrapperHeight = ref(0);
    const observer = ref(null);

    // 计算列数和列宽
    const calculateColumns = () => {
      if (!waterfallWrapper.value) return;

      wrapperWidth.value = waterfallWrapper.value.clientWidth;

      // 根据断点确定列数
      const sortedBreakpoints = Object.keys(props.breakpoints)
        .map(Number)
        .sort((a, b) => b - a); // 从大到小排序

      let foundCols = 1;
      for (const breakpoint of sortedBreakpoints) {
        if (wrapperWidth.value >= breakpoint) {
          foundCols = props.breakpoints[breakpoint].rowPerView;
          break;
        }
      }

      cols.value = foundCols;

      // 计算列宽
      if (props.hasAroundGutter) {
        colWidth.value = (wrapperWidth.value - props.gutter * 2 - (cols.value - 1) * props.gutter) / cols.value;
        offsetX.value = props.gutter;
      } else {
        colWidth.value = (wrapperWidth.value - (cols.value - 1) * props.gutter) / cols.value;
        offsetX.value = 0;
      }

      // 处理对齐方式
      if (props.align === 'center') {
        const totalWidth = cols.value * colWidth.value + (cols.value - 1) * props.gutter;
        offsetX.value = (wrapperWidth.value - totalWidth) / 2;
      } else if (props.align === 'right') {
        const totalWidth = cols.value * colWidth.value + (cols.value - 1) * props.gutter;
        offsetX.value = wrapperWidth.value - totalWidth;
      }
    };

    // 加载图片
    const loadImage = (img) => {
      const url = img.dataset?.src || img.getAttribute('data-src');
      if (url) {
        img.src = url;
        if (props.crossOrigin) {
          img.crossOrigin = 'anonymous';
        }
        return true;
      }
      return false;
    };

    // 加载所有图片
    const loadAllImages = () => {
      if (!waterfallWrapper.value) return;

      const images = waterfallWrapper.value.querySelectorAll('img');
      images.forEach(img => {
        // 如果已经是加载状态则跳过
        if (img.src && !img.src.includes(props.loadingImg)) return;

        // 尝试加载图片
        const loaded = loadImage(img);

        // 设置错误处理
        if (loaded && props.errorImg) {
          img.onerror = () => {
            img.src = props.errorImg;
          };
        }
      });
    };

    // 初始化懒加载
    const initLazyLoad = () => {
      if (!waterfallWrapper.value) return;

      // 清理旧的观察器
      if (observer.value) {
        observer.value.disconnect();
      }

      // 创建新的观察器
      observer.value = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            const img = entry.target;
            if (img.dataset?.src || img.getAttribute('data-src')) {
              // 设置loading占位图
              if (props.loadingImg) {
                img.src = props.loadingImg;
              }

              // 延迟加载实际图片
              setTimeout(() => {
                loadImage(img);
                img.onload = () => debouncedLayout();

                if (props.errorImg) {
                  img.onerror = () => {
                    img.src = props.errorImg;
                  };
                }

                // 移除data-src属性
                img.removeAttribute('data-src');
              }, props.delay);
            }
            observer.value.unobserve(img);
          }
        });
      }, { threshold: 0.01 });

      // 观察所有懒加载图片
      const lazyImages = waterfallWrapper.value.querySelectorAll('img[data-src]');
      lazyImages.forEach(img => {
        observer.value.observe(img);
      });
    };

    // 计算布局
    const calculateLayout = () => {
      if (!waterfallWrapper.value || cols.value === 0) return;

      const items = waterfallWrapper.value.querySelectorAll('.waterfall-item');
      if (items.length === 0) return;

      const columnHeights = new Array(cols.value).fill(0);
      let maxHeight = 0;

      items.forEach((item, index) => {
        let minColHeight = columnHeights[0];
        let colIndex = 0;

        for (let i = 1; i < cols.value; i++) {
          if (columnHeights[i] < minColHeight) {
            minColHeight = columnHeights[i];
            colIndex = i;
          }
        }

        const x = colIndex * (colWidth.value + props.gutter) + offsetX.value;
        const y = columnHeights[colIndex];

        item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
        item.style.width = `${colWidth.value}px`;
        item.style.position = 'absolute';
        item.style.left = '0';
        item.style.top = '0';
        item.style.visibility = 'visible';

        // 计算项目高度（包含所有图片）
        const itemHeight = item.offsetHeight || 200;
        columnHeights[colIndex] += itemHeight + props.gutter;

        // 更新最大高度
        if (columnHeights[colIndex] > maxHeight) {
          maxHeight = columnHeights[colIndex];
        }
      });

      wrapperHeight.value = maxHeight;
      emit('afterRender');
    };

    // 防抖函数
    const debounce = (fn, delay) => {
      let timeoutId;
      return (...args) => {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), delay);
      };
    };

    const debouncedLayout = debounce(() => {
      calculateColumns();
      calculateLayout();
    }, props.posDuration);

    // 初始化
    onMounted(() => {
      if (!waterfallWrapper.value) return;

      calculateColumns();

      nextTick(() => {
        if (props.lazyLoad) {
          initLazyLoad();
        } else {
          loadAllImages();
        }
        calculateLayout();
      });

      window.addEventListener('resize', debouncedLayout);
    });

    // 清理
    onUnmounted(() => {
      if (observer.value) {
        observer.value.disconnect();
      }
      window.removeEventListener('resize', debouncedLayout);
    });

    // 监听数据变化
    watch(() => props.list, () => {
      debouncedLayout();
      nextTick(() => {
        if (props.lazyLoad) {
          initLazyLoad();
        } else {
          loadAllImages();
        }
      });
      // calculateLayout();
    }, { deep: true });

    // 提供刷新方法
    provide('refreshWaterfall', debouncedLayout);

    const getRenderURL = (item) => {
      return item[props.imgSelector];
    };

    const getKey = (item, index) => {
      return item[props.rowKey] || index;
    };

    return {
      waterfallWrapper,
      wrapperHeight,
      getRenderURL,
      getKey
    };
  }
};
</script>

<style scoped>
.waterfall-list {
  position: relative;
  width: 100%;
  margin: 0 auto;
}

.waterfall-item {
  position: absolute;
  visibility: hidden;
  transition: transform 0.3s ease;
  will-change: transform;
  box-sizing: border-box;
}
</style>